
/*
* Copyright 2010, Center for Advanced Information Systems,Nanyang Technological University
*
* File name: DemoGraphScene.java
*
* Abstract:  Response the mouse actions on the plane, such as addition, deletion
*
* Current Version:      0.1
* Auther:               Jin Changjiu
* Modified Date:        Jun.16,2009
*
*/
package gblend.util;

import java.awt.Color;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.sql.SQLException;
import org.netbeans.api.visual.action.AcceptProvider;
import org.netbeans.api.visual.action.ActionFactory;
import org.netbeans.api.visual.action.ConnectorState;
import org.netbeans.api.visual.action.TwoStateHoverProvider;
import org.netbeans.api.visual.action.WidgetAction;
import org.netbeans.api.visual.anchor.AnchorFactory;
import org.netbeans.api.visual.anchor.AnchorShape;
import org.netbeans.api.visual.border.Border;
import org.netbeans.api.visual.border.BorderFactory;
import org.netbeans.api.visual.graph.GraphScene;
import org.netbeans.api.visual.widget.ConnectionWidget;
import org.netbeans.api.visual.widget.LabelWidget;
import org.netbeans.api.visual.widget.LayerWidget;
import org.netbeans.api.visual.widget.Widget;
import org.openide.util.Exceptions;
import gblend.exactquery.BuildQuery;
import java.util.Iterator;
import java.util.Vector;

/**
 * @author cjjin
 */
public class DemoGraphScene extends GraphScene.StringGraph {

    private static final Border BORDER_4 = BorderFactory.createLineBorder (4);
   
    private LayerWidget mainLayer;
    private LayerWidget connectionLayer;
   
    private ConnectionWidget [] conSet = new ConnectionWidget[100];
    private String dragnode;
    private int num = 0;
    private WidgetAction selectAction = createSelectAction ();
    private WidgetAction moveAction = ActionFactory.createMoveAction ();
    private WidgetAction hoverAction = ActionFactory.createHoverAction (new MyHoverProvider ());
    private DrawEdgeAction drawEdge = new DrawEdgeAction();
    private BuildQuery buildquery = new BuildQuery();
    private Vector<QueryEdge> edgeSet = new Vector<QueryEdge>();

    public DemoGraphScene() {

        mainLayer = new LayerWidget (this);
        addChild(mainLayer);
        connectionLayer = new LayerWidget (this);
        addChild(connectionLayer);
       
       for(int i = 0;i<100;i++)
       {
            conSet[i]= new ConnectionWidget(this);
       }
       
        getActions().addAction(hoverAction);    
        getActions().addAction(ActionFactory.createZoomAction());
        getActions().addAction(ActionFactory.createPanAction ());
        getActions().addAction(drawEdge);
        getActions().addAction(ActionFactory.createAcceptAction(new AcceptProvider() {
           
        public ConnectorState isAcceptable(Widget widget, Point point, Transferable transferable) {
                try {
                    dragnode = (String) transferable.getTransferData(DataFlavor.stringFlavor);
                } catch (UnsupportedFlavorException ex) {
                    Exceptions.printStackTrace(ex);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
       
            return ConnectorState.ACCEPT;
        }

    public void accept(Widget widget, Point point, Transferable transferable) {
       
       try {
            dragnode = (String) transferable.getTransferData(DataFlavor.stringFlavor);
           
           } catch (UnsupportedFlavorException ex) {
                  Exceptions.printStackTrace(ex);
           } catch (IOException ex) {
                  Exceptions.printStackTrace(ex);
           }
       
         Widget w = DemoGraphScene.this.addNode(dragnode);
                
         w.setPreferredLocation(widget.convertLocalToScene(point));
                
         buildquery.recordNodes(w.hashCode(), dragnode);
                
    }         
    }));

   }
    

  public  BuildQuery getQueryIndex()
  {
        return buildquery;
  }


 public LayerWidget getConLayer()
   {
        return connectionLayer;
   }
   public ConnectionWidget getConWidget(int index)
   {
        return conSet[index];
   }
    protected Widget attachNodeWidget (String node) {
        
       LabelWidget label = new LabelWidget (this, node);
       label.setBorder (BORDER_4);
       label.setBackground (new Color(170,226,255));
       label.setOpaque (true);
       label.setLabel (node);
       label.getActions().addAction(drawEdge);
       //single-click, the event is not consumed:
       label.getActions().addAction(selectAction); 
       //mouse-dragged, the event is consumed while mouse is dragged:
       label.getActions().addAction(moveAction); 
       //mouse-over, the event is consumed while the mouse is over the widget:
       label.getActions().addAction(hoverAction); 
        mainLayer.addChild(label);    
        return label;

    }

    protected Widget attachEdgeWidget (String edge)
    {
        ConnectionWidget connection = new ConnectionWidget (this);
        connection.setTargetAnchorShape (AnchorShape.NONE);
        connectionLayer.addChild (connection);
        return connection;
    }

    protected void attachEdgeSourceAnchor (String edge, String oldSourceNode, String sourceNode) {
        Widget w = sourceNode != null ? findWidget (sourceNode) : null;
        ((ConnectionWidget) findWidget (edge)).setSourceAnchor (AnchorFactory.createRectangularAnchor (w));
    }

    protected void attachEdgeTargetAnchor (String edge, String oldTargetNode, String targetNode) {
        Widget w = targetNode != null ? findWidget (targetNode) : null;
        ((ConnectionWidget) findWidget (edge)).setTargetAnchor (AnchorFactory.createRectangularAnchor (w));
    }

    
    private static class MyHoverProvider implements TwoStateHoverProvider {

        public void unsetHovering (Widget widget) {
            widget.setBackground (new Color(170,226,255));
            widget.setForeground (Color.BLACK);
        }

        public void setHovering (Widget widget) {
            widget.setBackground (new Color (164,211,238));
            widget.setForeground (Color.WHITE);
        }

    }
    
  
   public class DrawEdgeAction extends WidgetAction.Adapter {

        public Widget w1=null,w2=null;
     
        @Override
        public State mouseClicked (Widget widget, WidgetMouseEvent event) {
            if (event.getButton () == MouseEvent.BUTTON1 && w1==null) {
                w1 =  widget; 
               
                return State.CONSUMED;
            } else if (event.getButton () == MouseEvent.BUTTON3 && w1!=null) {
               w2 =   widget;
              try {
                 try {
                        drawEdge(w1, w2);
                        int edgeid =  buildquery.recordEdge(w1.hashCode(),w2.hashCode());
                        
                        if(edgeid!=0) //if one edge is suggested to users, change the nodes colour
                        {
                            int srccode = edgeSet.elementAt(edgeid-1).getSrc();
                            int trgcode = edgeSet.elementAt(edgeid-1).getTrg();
                            for(int i = 0; i<mainLayer.getChildren().size(); i++)
                            {
                                Widget node = mainLayer.getChildren().get(i);
                                if(node.hashCode()==srccode || node.hashCode()==trgcode)
                                {
                                    node.setBackground (new Color(244,72,53));
                                }
                            }
                        }
                        else //set the default colour back
                        {
                              for(int i = 0; i<mainLayer.getChildren().size(); i++)
                              {
                                  Widget node = mainLayer.getChildren().get(i);
                                  node.setBackground (new Color(170,226,255));
                               }
                        }

                    } catch (ClassNotFoundException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                } catch (SQLException ex) {
                    Exceptions.printStackTrace(ex);
                }
               w1=null;
               w2=null;
              return State.CONSUMED;
            }
            //delete the edge with its end node
            else if (event.getButton () == MouseEvent.BUTTON3 && w1==null) {
                w1 =  widget;

                return State.CONSUMED;
            }
            else if(event.getButton() == MouseEvent.BUTTON1 && w1!=null)
            {
                w2 = widget;
                try {

                    deleteAction(w1, w2);
                    
                    } catch (SQLException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (ClassNotFoundException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                    w1=null;
                    w2=null;

                    return State.CONSUMED;
            }

            return State.REJECTED;
        }

       public void drawEdge(Widget widget1, Widget widget2) throws SQLException, ClassNotFoundException
       {
            ConnectionWidget conwid=conSet[num];
            conwid.setSourceAnchor (AnchorFactory.createRectangularAnchor (widget1));
            conwid.setTargetAnchor (AnchorFactory.createRectangularAnchor (widget2));
            conwid.setTargetAnchorShape (AnchorShape.NONE);
            connectionLayer.addChild (conwid);
            
            QueryEdge qedge = new QueryEdge();
            qedge.setSrc(widget1.hashCode());
            qedge.setTrg(widget2.hashCode());
            qedge.setId(num++);
            edgeSet.addElement(qedge);
        }

       //delete edge with its end node
        public void deleteAction(Widget widget1, Widget widget2)
                throws SQLException, ClassNotFoundException {
            int edgeid=-1;
            for(Iterator itr = edgeSet.iterator();itr.hasNext();)
            {
                 QueryEdge qedge = (QueryEdge)itr.next();

                 if(qedge.getSrc()==widget1.hashCode()&&qedge.getTrg()==widget2.hashCode()
                         ||qedge.getSrc()==widget2.hashCode()&&qedge.getTrg()==widget1.hashCode())
                 {
                     ConnectionWidget conwid=conSet[qedge.getId()];
                     connectionLayer.removeChild(conwid);
                     edgeid = qedge.getId();
                     itr.remove();
                     break;
                 }
            }

            //delete the node without connected edges
            int k = testExistingNode(widget1, widget2);
            Widget removedWidget = null;
            if(k==1)
            {
                removedWidget = widget1;
            }
            else if(k==2)
            {
                removedWidget = widget2;
            }

            if(removedWidget!=null)//one node is deleted
            {
                mainLayer.removeChild(removedWidget);
                buildquery.modifyQuery(removedWidget.hashCode(), edgeid);
            }
            else
            {
                buildquery.modifyQuery(0, edgeid);//only the edge is deleted
            }
            
       }

       //test the node without inputs
       public int testExistingNode(Widget widget1, Widget widget2)
       {
           boolean f1= false, f2=false;
            for(int i = 0; i< edgeSet.size(); i++)
            {
                  QueryEdge qedge = edgeSet.elementAt(i);

                  if(qedge.getSrc()==widget1.hashCode()||qedge.getTrg()==widget1.hashCode())
                  {
                        f1=true;
                  }

                  if(qedge.getSrc()==widget2.hashCode()||qedge.getTrg()==widget2.hashCode())
                  {
                        f2=true;
                  }
            }

            if(f1==false)
            {
                return 1;
            }
            else if(f2==false)
            {
                return 2;
            }

           return -1;
       }
       
   }

    
}
